using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace Identity.Api.Data;

public sealed class SeedDataHostedService(IServiceProvider serviceProvider, ILogger<SeedDataHostedService> logger) : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await using var scope = serviceProvider.CreateAsyncScope();
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        await context.Database.EnsureCreatedAsync(cancellationToken);
        await CreateAppsAsync(scope.ServiceProvider);
        await CreateScopesAsync(scope.ServiceProvider);
        logger.LogWarning("OpenIddict 种子数据已初始化完成");
    }

    private async Task CreateAppsAsync(IServiceProvider provider)
    {
        var manager = provider.GetRequiredService<IOpenIddictApplicationManager>();

        async Task EnsureApplicationAsync(string clientId, Func<OpenIddictApplicationDescriptor> descriptorFactory)
        {
            var existing = await manager.FindByClientIdAsync(clientId);
            var descriptor = descriptorFactory();

            if (existing is null)
            {
                await manager.CreateAsync(descriptor);
                return;
            }

            await manager.UpdateAsync(existing, descriptor);
        }

        await EnsureApplicationAsync("vite_bff_host", () => new OpenIddictApplicationDescriptor
        {
            ClientId = "vite_bff_host",
            ClientSecret = "codeflow_pkce_client_secret",
            ClientType = ClientTypes.Confidential,
            ConsentType = ConsentTypes.Explicit,
            DisplayName = "Bff code PKCE",
            RedirectUris =
            {
                new Uri("https://localhost:7228/signin-oidc"),
                new Uri("http://localhost:5014/signin-oidc"),
                // 其他需要的回调
            },
            PostLogoutRedirectUris =
            {
                new Uri("https://localhost:7228/signout-callback-oidc"),
                new Uri("http://localhost:5014/signout-callback-oidc"),
            },
            Permissions =
            {
                Permissions.Endpoints.Authorization,
                Permissions.Endpoints.Token,
                Permissions.Endpoints.Revocation,
                Permissions.Endpoints.EndSession,
                Permissions.GrantTypes.AuthorizationCode,
                Permissions.GrantTypes.RefreshToken,
                Permissions.ResponseTypes.Code,

                Permissions.Scopes.Email,
                Permissions.Scopes.Profile,
                Permissions.Scopes.Roles,
                Permissions.Prefixes.Scope + "bff_acceess_scope"
            },
            Requirements =
            {
                Requirements.Features.ProofKeyForCodeExchange
            }
        });

        await EnsureApplicationAsync("res_server", () => new OpenIddictApplicationDescriptor
        {
            ClientId = "res_server",
            ClientSecret = "res_server_secret",
            ClientType = ClientTypes.Confidential,
            Permissions =
            {
                Permissions.Endpoints.Introspection
            }
        });

        await EnsureApplicationAsync("shopping_web", () => new OpenIddictApplicationDescriptor
        {
            ClientId = "shopping_web",
            ClientType = ClientTypes.Public,
            ConsentType = ConsentTypes.Explicit,
            DisplayName = "Shopping SPA",
            RedirectUris =
            {
                new Uri("http://localhost:5173/"),
                new Uri("https://localhost:5173/"),
                new Uri("http://localhost:5173/signin-oidc"),
                new Uri("https://localhost:5173/signin-oidc"),
            },
            PostLogoutRedirectUris =
            {
                new Uri("http://localhost:5173/"),
                new Uri("https://localhost:5173/"),
                new Uri("http://localhost:5173/signout-callback-oidc"),
                new Uri("https://localhost:5173/signout-callback-oidc"),
            },
            Permissions =
            {
                Permissions.Endpoints.Authorization,
                Permissions.Endpoints.Token,
                Permissions.GrantTypes.AuthorizationCode,
                Permissions.GrantTypes.RefreshToken,
                Permissions.ResponseTypes.Code,

                Permissions.Scopes.Email,
                Permissions.Scopes.Profile,
                Permissions.Scopes.Roles,
                Permissions.Prefixes.Scope + "res_server"
            },
            Requirements =
            {
                Requirements.Features.ProofKeyForCodeExchange
            }
        });
    }

    private async Task CreateScopesAsync(IServiceProvider provider)
    {
        var manager = provider.GetRequiredService<IOpenIddictScopeManager>();

        async Task EnsureScopeAsync(string name, Func<OpenIddictScopeDescriptor> descriptorFactory)
        {
            if (await manager.FindByNameAsync(name) is not null)
            {
                return;
            }

            await manager.CreateAsync(descriptorFactory());
        }

        await EnsureScopeAsync("bff_acceess_scope", () => new OpenIddictScopeDescriptor
        {
            Name = "bff_acceess_scope",
            DisplayName = "bff_acceess_scope",
            Resources =
            {
                "res_server"
            }
        });

        await EnsureScopeAsync("res_server", () => new OpenIddictScopeDescriptor
        {
            Name = "res_server",
            DisplayName = "Resource Server scope",
            Resources =
            {
                "res_server"
            }
        });
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
